package models

import (
	"gitchina/kly_service/data"
	"gitchina/kly_service/errors"
	"gitchina/kly_service/util"
	"strconv"
	"time"
)

var recommendLineCache = util.NewCache(util.Recommend_Line_Cache, util.CacheType(), 5*time.Minute)

type LinePubRQ struct {
	StartArea       string  `json:"start_area"`
	EndArea         string  `json:"end_area"`
	TermOfValidity  int     `json:"term_of_validity"`
	HeavyCargoPrice float32 `json:"heavy_cargo_price"`
	LightCargoPrice float32 `json:"light_cargo_price"`
	BasePrice       float32 `json:"base_price"`
}

type LineUpdateRQ struct {
	Id              ID      `json:"line_pub_id"`
	StartArea       string  `json:"start_area"`
	EndArea         string  `json:"end_area"`
	TermOfValidity  int     `json:"term_of_validity"`
	HeavyCargoPrice float32 `json:"heavy_cargo_price"`
	LightCargoPrice float32 `json:"light_cargo_price"`
	BasePrice       float32 `json:"base_price"`
}

type GetLinePubRQ struct {
	Id         ID     `json:"emp_id"`
	QueryState string `json:"query_state"`
	PageNo     int    `json:"page_no"`
	PageSize   int    `json:"page_size"`
}

type LinePubRS struct {
	TotalCount int          `json:"total_count"`
	List       []GetLinePub `json:"pub_list"`
}

type GetLinePub struct {
	LinePubId       string  `json:"line_pub_id"`
	StartArea       string  `json:"start_area"`
	EndArea         string  `json:"end_area"`
	TermOfValidity  int     `json:"term_of_validity"`
	HeavyCargoPrice float32 `json:"heavy_cargo_price"`
	LightCargoPrice float32 `json:"light_cargo_price"`
	State           int     `json:"state"`
	BasePrice       float32 `json:"base_price"`
}

type QueryLinePubListRQ struct {
	StartArea       string `json:"start_area"`
	EndArea         string `json:"end_area"`
	CorpName        string `json:"corp_name"`
	HeavyCargoPrice string `json:"heavy_cargo_price"`
	LightCargoPrice string `json:"light_cargo_price"`
	PageNo          int    `json:"page_no"`
	PageSize        int    `json:"page_size"`
}

type LinePubListRS struct {
	TotalCount int           `json:"total_count"`
	List       []LinePubList `json:"pub_list"`
}

type LinePubList struct {
	OwnerName       string  `json:"owner_name"`
	OwnerPhone      string  `json:"owner_phone"`
	OwnerId         ID      `json:"owner_id"`
	OwnerPhoto      string  `json:"owner_photo"`
	CorpName        string  `json:"corp_name"`
	LinePubId       ID      `json:"line_pub_id"`
	StartArea       string  `json:"start_area"`
	EndArea         string  `json:"end_area"`
	HeavyCargoPrice float32 `json:"heavy_cargo_price"`
	LightCargoPrice float32 `json:"light_cargo_price"`
	BasePrice       float32 `json:"base_price"`
	State           int     `json:"state"`
	TermOfValidity  int     `json:"term_of_validity"`
}

type LinePubDetailRS struct {
	OwnerName       string  `json:"owner_name"`
	OwnerPhone      string  `json:"owner_phone"`
	OwnerPhoto      string  `json:"owner_photo"`
	OwnerId         ID      `json:"owner_id"`
	CorpName        string  `json:"corp_name"`
	LinePubId       ID      `json:"line_pub_id"`
	StartArea       string  `json:"start_area"`
	EndArea         string  `json:"end_area"`
	Intro           string  `json:"intro"`
	HeavyCargoPrice float32 `json:"heavy_cargo_price"`
	LightCargoPrice float32 `json:"light_cargo_price"`
	BasePrice       float32 `json:"base_price"`
	State           int     `json:"state"`
	TermOfValidity  int     `json:"term_of_validity"`
}

type GetRecommendLineListRQ struct {
	StartArea int `json:"start_area"`
	PageSize  int `json:"page_size"`
}

type RecommendLineListRS struct {
	Id          ID     `json:"line_pub_id"`
	EmpPhotoUrl string `json:"photo_url"`
	CorpName    string `json:"corp_name"`
	StartArea   string `json:"start_area"`
	EndArea     string `json:"end_area"`
}

func (l Logistics) LinePub(mbrId ID, rq *LinePubRQ) (pubId ID, err error) {
	var id int64
	var authState string
	var srcCounty, srcCity, srcProvince, destCounty, destCity, destProvince int
	var repo data.LogisticsRepo
	var mbrRepo data.MemberRepo

	if srcCounty, err = strconv.Atoi(rq.StartArea); err != nil || srcCounty == 0 {
		err = errors.New(errors.MSG_Logi_Invalid_Start_Area)
		return
	}

	if destCounty, err = strconv.Atoi(rq.EndArea); err != nil || destCounty == 0 {
		err = errors.New(errors.MSG_Logi_Invalid_End_Area)
		return
	}

	if rq.TermOfValidity <= 0 {
		err = errors.New(errors.MSG_Logi_Invalid_TermOfValidity)
		return
	}

	if _, _, _, id, err = mbrId.Decode(); err != nil {
		err = errors.New(errors.MSG_Mbr_Invalid)
		return
	}

	if srcCounty%10000 == 0 {
		srcProvince = srcCounty
		srcCity = 0
		srcCounty = 0
	} else if srcCounty%100 == 0 {
		srcProvince = srcCounty - srcCounty%10000
		srcCity = srcCounty
		srcCounty = 0
	} else {
		srcProvince = srcCounty - srcCounty%10000
		srcCity = srcCounty - srcCounty%100
	}

	if destCounty%10000 == 0 {
		destProvince = destCounty
		destCity = 0
		destCounty = 0
	} else if destCounty%100 == 0 {
		destProvince = destCounty - destCounty%10000
		destCity = destCounty
		destCounty = 0
	} else {
		destProvince = destCounty - destCounty%10000
		destCity = destCounty - destCounty%100
	}

	authState, err = mbrRepo.GetEntiAuthState(int(id), data.Mbr_logistics_type)
	if err != nil {
		util.GetLogger().Error("[model-LinePub] - error:%s", err.Error())
		err = errors.New(errors.MSG_Logi_Line_Pub_Add_Error)
		return
	}

	if authState != "2" {
		err = errors.New(errors.MSG_Mbr_Upd_Info_Not_Allowed)
		return
	}

	id, err = repo.AddLinePub(&data.LinePubDto{
		MemberId:        int(id),
		SrcProvince:     srcProvince,
		SrcCity:         srcCity,
		SrcCounty:       srcCounty,
		DestProvince:    destProvince,
		DestCity:        destCity,
		DestCounty:      destCounty,
		HeavyCargoPrice: rq.HeavyCargoPrice,
		LightCargoPrice: rq.LightCargoPrice,
		BasePrice:       rq.BasePrice,
		TermOfValidity:  rq.TermOfValidity,
		State:           0,
	})

	if err == nil {
		if id == 0 {
			err = errors.New(errors.MSG_Logi_Line_Exists)
		} else {
			pubId.Encode(ID_Shard, ID_Ver, ID_Carrier_Pub_Category, id)
		}
	} else {
		util.GetLogger().Error("[model] - line pub error: %s", err.Error())
		err = errors.New(errors.MSG_Logi_Line_Pub_Add_Error)
	}

	return
}

func (l Logistics) UpdateLine(mbrId ID, rq *LineUpdateRQ) (err error) {
	var pId, mId int64
	var srcCounty, srcCity, srcProvince, destCounty, destCity, destProvince int
	var repo data.LogisticsRepo

	if srcCounty, err = strconv.Atoi(rq.StartArea); err != nil || srcCounty == 0 {
		err = errors.New(errors.MSG_Logi_Invalid_Start_Area)
		return
	}

	if destCounty, err = strconv.Atoi(rq.EndArea); err != nil || destCounty == 0 {
		err = errors.New(errors.MSG_Logi_Invalid_End_Area)
		return
	}

	if rq.TermOfValidity <= 0 {
		err = errors.New(errors.MSG_Logi_Invalid_TermOfValidity)
		return
	}

	if _, _, _, mId, err = mbrId.Decode(); err != nil {
		err = errors.New(errors.MSG_Mbr_Invalid)
		return
	}

	if _, _, _, pId, err = rq.Id.Decode(); err != nil {
		err = errors.New(errors.MSG_Logi_Invalid_Line_ID)
		return
	}

	if srcCounty%10000 == 0 {
		srcProvince = srcCounty
		srcCity = 0
		srcCounty = 0
	} else if srcCounty%100 == 0 {
		srcProvince = srcCounty - srcCounty%10000
		srcCity = srcCounty
		srcCounty = 0
	} else {
		srcProvince = srcCounty - srcCounty%10000
		srcCity = srcCounty - srcCounty%100
	}

	if destCounty%10000 == 0 {
		destProvince = destCounty
		destCity = 0
		destCounty = 0
	} else if destCounty%100 == 0 {
		destProvince = destCounty - destCounty%10000
		destCity = destCounty
		destCounty = 0
	} else {
		destProvince = destCounty - destCounty%10000
		destCity = destCounty - destCounty%100
	}

	err = repo.UpdateLine(&data.LinePubDto{
		Id:              int(pId),
		MemberId:        int(mId),
		SrcProvince:     srcProvince,
		SrcCity:         srcCity,
		SrcCounty:       srcCounty,
		DestProvince:    destProvince,
		DestCity:        destCity,
		DestCounty:      destCounty,
		HeavyCargoPrice: rq.HeavyCargoPrice,
		LightCargoPrice: rq.LightCargoPrice,
		BasePrice:       rq.BasePrice,
		TermOfValidity:  rq.TermOfValidity,
	})

	if err != nil {
		util.GetLogger().Error("[model] - line pub error: %s", err.Error())
		err = errors.New(errors.MSG_Logi_Line_Pub_Update_Error)
	}

	return
}

func (l Logistics) CancelLine(mbrId, lineId ID) {
	var err error
	var repo data.LogisticsRepo
	var mId, lId int64
	if _, _, _, mId, err = mbrId.Decode(); err != nil {
		err = errors.New(errors.MSG_Mbr_Invalid)
		return
	}

	if _, _, _, lId, err = lineId.Decode(); err != nil {
		err = errors.New(errors.MSG_Mbr_Invalid)
		return
	}

	if err = repo.CancelLine(int(mId), int(lId)); err != nil {
		util.GetLogger().Error("[model-CancelLine] - error:%s", err.Error())
	}
}

func (l Logistics) RecoverLine(mbrId, lineId ID) {

	var err error
	var repo data.LogisticsRepo
	var mId, lId int64
	if _, _, _, mId, err = mbrId.Decode(); err != nil {
		err = errors.New(errors.MSG_Mbr_Invalid)
		return
	}

	if _, _, _, lId, err = lineId.Decode(); err != nil {
		err = errors.New(errors.MSG_Mbr_Invalid)
		return
	}

	if err = repo.RecoverLine(int(mId), int(lId)); err != nil {
		util.GetLogger().Error("[model-RecoverLine] - error:%s", err.Error())
	}

}

func (l Logistics) GetLinePubs(mbrId ID, rq GetLinePubRQ) (rs LinePubRS, err error) {
	var rowCount int
	var list []data.LinePubDto
	var repo data.LogisticsRepo
	_, _, _, id, err := mbrId.Decode()
	if err != nil {
		err = errors.New(errors.MSG_Mbr_Invalid)
	} else {
		if rq.PageNo <= 0 {
			rq.PageNo = 1
		}

		if rq.PageSize <= 0 || rq.PageSize > 20 {
			rq.PageSize = 10
		}

		if list, rowCount, err = repo.GetLinePubs(int(id), rq.PageNo, rq.PageSize, rq.QueryState); err == nil {
			pubs := make([]GetLinePub, len(list))
			for idx, dto := range list {
				var linePubId ID
				linePubId.Encode(ID_Shard, ID_Ver, ID_Line_Pub_Category, int64(dto.Id))

				pubs[idx] = GetLinePub{
					LinePubId:       string(linePubId),
					HeavyCargoPrice: dto.HeavyCargoPrice,
					LightCargoPrice: dto.LightCargoPrice,
					BasePrice:       dto.BasePrice,
					TermOfValidity:  dto.TermOfValidity,
					State:           dto.State,
				}

				pubs[idx].StartArea = l.chooseArea(dto.SrcProvince, dto.SrcCity, dto.SrcCounty)
				pubs[idx].EndArea = l.chooseArea(dto.DestProvince, dto.DestCity, dto.DestCounty)
			}
			rs = LinePubRS{TotalCount: rowCount, List: pubs}
		} else {
			util.GetLogger().Error("[model-GetLinePubs] - error:%s", err.Error())
			err = nil
		}
	}
	return
}

func (l Logistics) QueryLinePubList(rq QueryLinePubListRQ) (rs LinePubListRS, err error) {
	var list []data.LinePubListDto
	var repo data.LogisticsRepo
	var rowCount int
	param := l.getQueryLinePubListCond(rq)
	if list, rowCount, err = repo.QueryLinePubList(param); err == nil {
		pubs := make([]LinePubList, len(list))
		for idx, dto := range list {
			var linePubId, mbrId ID
			linePubId.Encode(ID_Shard, ID_Ver, ID_Carrier_Pub_Category, int64(dto.LinePubId))
			mbrId.Encode(ID_Shard, ID_Ver, ID_Member_Category, int64(dto.OwnerId))

			pubs[idx] = LinePubList{
				OwnerName:       dto.OwnerName,
				OwnerPhone:      dto.OwnerPhone,
				OwnerId:         mbrId,
				OwnerPhoto:      dto.OwnerPhoto,
				CorpName:        dto.CorpName,
				LinePubId:       linePubId,
				HeavyCargoPrice: dto.HeavyCargoPrice,
				LightCargoPrice: dto.LightCargoPrice,
				BasePrice:       dto.BasePrice,
				TermOfValidity:  dto.TermOfValidity,
				State:           dto.State,
			}

			pubs[idx].StartArea = l.chooseArea(dto.SrcProvince, dto.SrcCity, dto.SrcCounty)
			pubs[idx].EndArea = l.chooseArea(dto.DestProvince, dto.DestCity, dto.DestCounty)
		}
		rs = LinePubListRS{TotalCount: rowCount, List: pubs}
	} else {
		util.GetLogger().Error("[model-QueryLinePubList] - error:%s", err.Error())
		err = nil
	}
	return
}

func (l Logistics) GetLinePubDetail(pubId ID) (rs LinePubDetailRS, err error) {
	var pub data.LinePubDetail
	var repo data.LogisticsRepo
	_, _, _, id, err := pubId.Decode()
	if err != nil {
		err = errors.New(errors.MSG_Logi_Invalid_Line_ID)
	} else {
		var mbrId ID
		if pub, err = repo.GetLinePubDetail(int(id)); err == nil {
			mbrId.Encode(ID_Shard, ID_Ver, ID_Member_Category, int64(pub.OwnerId))
			rs.OwnerName = pub.OwnerName
			rs.OwnerPhone = pub.OwnerPhone
			rs.OwnerId = mbrId
			rs.OwnerPhoto = pub.OwnerPhoto
			rs.LinePubId = pubId
			rs.HeavyCargoPrice = pub.HeavyCargoPrice
			rs.LightCargoPrice = pub.LightCargoPrice
			rs.BasePrice = pub.BasePrice
			rs.Intro = pub.Intro
			rs.TermOfValidity = pub.TermOfValidity
			rs.State = pub.State
			rs.CorpName = pub.CorpName
			rs.StartArea = l.chooseArea(pub.SrcProvince, pub.SrcCity, pub.SrcCounty)
			rs.EndArea = l.chooseArea(pub.DestProvince, pub.DestCity, pub.DestCounty)
		} else {
			util.GetLogger().Error("[model-GetLinePubDetail] - error:%s", err.Error())
			err = nil
		}
	}
	return
}

func (l Logistics) GetRecommendLineList(rq *GetRecommendLineListRQ) (rs []RecommendLineListRS) {
	var repo data.LogisticsRepo
	if rq.PageSize > 10 {
		rq.PageSize = 10
	}

	if val, ok := recommendLineCache.Get(strconv.Itoa(rq.StartArea)); !ok {
		list, _ := repo.GetRecommendLineList(rq.StartArea, rq.PageSize)
		rs = make([]RecommendLineListRS, len(list))
		for idx, dto := range list {
			item := RecommendLineListRS{}
			l.fillRecommendLine(&item, dto)
			rs[idx] = item
		}

		if len(rs) < rq.PageSize {
			if rq.StartArea%100 != 0 {
				rq.StartArea = rq.StartArea - rq.StartArea%100
				list, _ := repo.GetRecommendLineList(rq.StartArea, rq.PageSize-len(rs))
				for _, dto := range list {
					item := RecommendLineListRS{}
					l.fillRecommendLine(&item, dto)
					rs = append(rs, item)
				}
			}
		}

		if len(rs) < rq.PageSize {
			if rq.StartArea%10000 != 0 {
				rq.StartArea = rq.StartArea - rq.StartArea%10000
				list, _ := repo.GetRecommendLineList(rq.StartArea, rq.PageSize-len(rs))
				for _, dto := range list {
					item := RecommendLineListRS{}
					l.fillRecommendLine(&item, dto)
					rs = append(rs, item)
				}
			}
		}

		if len(rs) > 0 {
			recommendLineCache.Set(strconv.Itoa(rq.StartArea), rs)
		}
	} else {
		rs = val.([]RecommendLineListRS)
	}

	return
}

func (l Logistics) fillRecommendLine(rs *RecommendLineListRS, dto data.RecommendListDto) {
	var linePubId ID
	linePubId.Encode(ID_Shard, ID_Ver, ID_Line_Pub_Category, int64(dto.Id))
	rs.Id = linePubId
	rs.EmpPhotoUrl = dto.EmpPhotoUrl
	rs.CorpName = dto.CorpName

	rs.StartArea = l.chooseArea(dto.SrcProvince, dto.SrcCity, dto.SrcCounty)
	rs.EndArea = l.chooseArea(dto.DestProvince, dto.DestCity, dto.DestCounty)
}

func (l Logistics) getQueryLinePubListCond(rq QueryLinePubListRQ) data.QueryLinePubListParam {
	var param data.QueryLinePubListParam
	if startAreaId, err := strconv.Atoi(rq.StartArea); err == nil {
		param.StartArea = startAreaId
	}
	if endAreaId, err := strconv.Atoi(rq.EndArea); err == nil {
		param.EndArea = endAreaId
	}

	if _, err := strconv.ParseFloat(rq.HeavyCargoPrice, 32); err == nil {
		param.HeavyCargoPrice = rq.HeavyCargoPrice
	}
	if _, err := strconv.ParseFloat(rq.LightCargoPrice, 32); err == nil {
		param.LightCargoPrice = rq.LightCargoPrice
	}
	param.CorpName = rq.CorpName
	if rq.PageNo >= 1 {
		param.PageNo = rq.PageNo
	} else {
		param.PageNo = 1
	}
	if rq.PageSize >= 1 && rq.PageSize <= 20 {
		param.PageSize = rq.PageSize
	} else {
		param.PageSize = 10
	}

	return param
}
